﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace TwoPageView3 {
    public partial class MainWindow : Window {
        // vertical means Width <= Height
        // horizontal means Height < Width

        private enum ImageDisplayStatus {
            Collapsed,
            Available,
            BackToBrowse,
            NowLoading,
        }

        private enum ImageDisplayTarget {
            TwoPage0,
            TwoPage1,
            OnePage
        }

        private enum WindowOrientation {
            Horizontal,
            Vertical
        }

        private enum CloseupViewShowCommand {
            ShowOnePage,
            ShowTwoPage,
            BackToThumbBrowser
        }

        public enum ShowCommand {
            SpecifiedPage,
            NextPage,
            PrevPage,
        }

        private enum CloseupViewShowStatus {
            Idle,
            Loading,
            ShowOnePage,
            ShowTwoPage
        }

        private IEnumerator<TpvFileInfo> mFileEnumerator;
        private List<TpvFileInfo> mCloseupFileList = new List<TpvFileInfo>();
        private int mCurrentPage = 0;
        BitmapImage mBitmapImage0;
        BitmapImage mBitmapImage1;

        private CloseupViewShowStatus mCloseupViewShowStatus = CloseupViewShowStatus.Idle;

        class BitmapImageCacheEntry {
            public string Path { get { return mPath; } }
            public BitmapImage BitmapImageCached { get { return mBitmapImage; } }

            private string mPath;
            private BitmapImage mBitmapImage;

            public BitmapImageCacheEntry(BitmapImage bi, string path) {
                mBitmapImage = bi;
                mPath = path;
            }
        }
        List<BitmapImageCacheEntry> mBitmapImageCache = new List<BitmapImageCacheEntry>();
        private const int BITMAP_CACHE_NUM = 4;

        private void InitializeCloseupView() {
            ResizePanelToFillParent(mCloseupViewGrid);
        }

        private void UpdateViewsCloseupView() {
            mState = State.CloseupView;

            mTreeThumbBrowserGrid.Visibility = Visibility.Collapsed;
            mCloseupViewGrid.Visibility      = Visibility.Visible;

            switch (mCloseupViewShowStatus) {
            case CloseupViewShowStatus.Idle:
            case CloseupViewShowStatus.Loading:
                break;
            case CloseupViewShowStatus.ShowOnePage:
            case CloseupViewShowStatus.ShowTwoPage:
                ShowCloseupView(mCurrentPage, ShowCommand.SpecifiedPage);
                break;
            }
        }

        private void ShowCloseupView(int page, ShowCommand showCommand) {
            Console.WriteLine("ShowCloseupView({0}, {1})", page, showCommand);
            if (mCloseupViewShowStatus == CloseupViewShowStatus.Loading) {
                Console.WriteLine("ShowCloseupView() but now loading ignored");
                return;
            }

            mCloseupViewShowStatus = CloseupViewShowStatus.Loading;
            mCurrentPage = page;

            UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage0, ImageDisplayStatus.Collapsed);
            UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage1, ImageDisplayStatus.Collapsed);
            UpdateImageDisplayStatus(ImageDisplayTarget.OnePage,  ImageDisplayStatus.NowLoading);

            mImage0.Source = null;
            mImage1.Source = null;
            mImageSingle.Source = null;

            var uiThreadDispatcher = Dispatcher.CurrentDispatcher;

            Task.Factory.StartNew(new Action(() => {
                ReadAndShow(showCommand, uiThreadDispatcher);
            }));
        }

        private void ReadAndShow(ShowCommand showCommand, Dispatcher uiThreadDispatcher) {
            switch (showCommand) {
            case ShowCommand.SpecifiedPage:
                if (mWindowOrientation == WindowOrientation.Vertical) {
                    // Windows orientation is vertical.
                    // onepageview.
                    PrepareCloseupFileList(mCurrentPage);
                } else {
                    // Window orientation is horizontal.
                    // page view will be determined with the first image orientation
                    PrepareCloseupFileList(mCurrentPage + 1);
                }
                break;
            case ShowCommand.NextPage:
                if (mBitmapImage1 != null && mBitmapImage1.Height < mBitmapImage1.Width) {
                    // twopageview mode and second page is horizontal
                    // next page is onepageview. no need to call PrepareCloseupFileList()
                    ++mCurrentPage;
                } else if (mWindowOrientation == WindowOrientation.Vertical) {
                    // Window orientation is vertical.
                    // next page is onepageview.
                    ++mCurrentPage;
                    PrepareCloseupFileList(mCurrentPage);
                } else if (mCloseupViewShowStatus == CloseupViewShowStatus.ShowOnePage) {
                    // Window orientation is horizontal and currently onepageview.
                    // advance 1 page. next page view will be determined with the next image orientation
                    ++mCurrentPage;
                    PrepareCloseupFileList(mCurrentPage + 1);
                } else {
                    // window orientation is horizontal and currently twopageview and second image is vertical.
                    // advance 2 page. next page view will be determined with the next image orientation
                    mCurrentPage += 2;
                    PrepareCloseupFileList(mCurrentPage + 1);
                }
                break;
            case ShowCommand.PrevPage:
                if (mWindowOrientation == WindowOrientation.Vertical) {
                    // Window orientation is vertical.
                    // onepageview.
                    --mCurrentPage;
                } else if (mCloseupViewShowStatus == CloseupViewShowStatus.ShowTwoPage
                        && mBitmapImage0 != null && mBitmapImage0.Height < mBitmapImage0.Width) {
                    // Window orientation is horizontal and currently twopageview and first image is horizontal.
                    // onepageview.
                    --mCurrentPage;
                } else {
                    // window orientation is horizontal and (currently onepage or twopageview and first image is vertical.)
                    // next page view will be determined with the mCurrentPage-1 image orientation
                    if (mCurrentPage-1 < 0) {
                        if (mCloseupViewShowStatus == CloseupViewShowStatus.ShowOnePage) {
                            --mCurrentPage;
                        } else {
                            mCurrentPage -= 2;
                        }
                    } else {
                        BitmapImage bi = CreateBitmapImageFromFileOrCache(mCloseupFileList[mCurrentPage-1].ActualPath);
                        if (bi != null && bi.Height < bi.Width) {
                            // next image is horizontal.
                            // onepageview
                            --mCurrentPage;
                        } else {
                            // next image is vertical
                            if (mCurrentPage - 2 < 0) {
                                // next image is vertical and the first page.
                                // twopageview
                                mCurrentPage -= 2;
                            } else {
                                BitmapImage bi1 = CreateBitmapImageFromFileOrCache(mCloseupFileList[mCurrentPage - 2].ActualPath);
                                if (bi1 != null && bi1.Height < bi1.Width) {
                                    // next image is vertical and next next page is horizontal.
                                    // twopageview
                                    --mCurrentPage;
                                } else {
                                    // next two images are vertical.
                                    // twopageview
                                    mCurrentPage -= 2;
                                }
                            }
                        }
                    }
                }
                break;
            }

            mBitmapImage0 = null;
            mBitmapImage1 = null;

            int page0 = mCurrentPage;
            int page1 = mCurrentPage + 1;

            CloseupViewShowCommand command = CloseupViewShowCommand.BackToThumbBrowser;

            if (0 <= page0 && page0 < mCloseupFileList.Count) {
                mBitmapImage0 = CreateBitmapImageFromFileOrCache(mCloseupFileList[page0].ActualPath);
                command = CloseupViewShowCommand.ShowOnePage;
            }

            if (mWindowOrientation == WindowOrientation.Horizontal && 
                    ((mBitmapImage0 != null && mBitmapImage0.Width <= mBitmapImage0.Height)
                     || (mBitmapImage0 == null && 0 <= page1 && page1 < mCloseupFileList.Count))) {
                if (0 <= page1 && page1 < mCloseupFileList.Count) {
                    mBitmapImage1 = CreateBitmapImageFromFileOrCache(mCloseupFileList[page1].ActualPath);
                    command = CloseupViewShowCommand.ShowTwoPage;
                }
            }

            uiThreadDispatcher.Invoke(new Action(() => {
                switch (command) {
                case CloseupViewShowCommand.BackToThumbBrowser:
                    ChangeViewToThumbnailBrowser();
                    break;
                case CloseupViewShowCommand.ShowOnePage:
                    CloseupViewShowOnePage();
                    break;
                case CloseupViewShowCommand.ShowTwoPage:
                    CloseupViewShowTwoPage();
                    break;
                }
            }));
        }

        private bool PrepareCloseupFileList(int page) {
            if (page < mCloseupFileList.Count) {
                return true;
            }

            for (int i = mCloseupFileList.Count; i <= page; ++i) {
                if (!mFileEnumerator.MoveNext()) {
                    return false;
                }
                mCloseupFileList.Add(mFileEnumerator.Current);
            }
            return true;
        }

        private void ChangeViewToThumbnailBrowser() {
            Console.WriteLine("ChangeViewToThumbnailBrowser");

            mCloseupViewShowStatus = CloseupViewShowStatus.Idle;
            UpdateViewsTreeThumbBrowse();
        }

        private void CloseupViewShowOnePage() {
            mCloseupViewShowStatus = CloseupViewShowStatus.ShowOnePage;

            UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage0, ImageDisplayStatus.Collapsed);
            UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage1, ImageDisplayStatus.Collapsed);

            if (mBitmapImage0 == null) {
                UpdateImageDisplayStatus(ImageDisplayTarget.OnePage, ImageDisplayStatus.BackToBrowse);
            } else {
                mImageSingle.Source = mBitmapImage0;
                UpdateImageDisplayStatus(ImageDisplayTarget.OnePage, ImageDisplayStatus.Available);
            }
        }
        
        private void CloseupViewShowTwoPage() {
            mCloseupViewShowStatus = CloseupViewShowStatus.ShowTwoPage;

            UpdateImageDisplayStatus(ImageDisplayTarget.OnePage, ImageDisplayStatus.Collapsed);

            if (mBitmapImage0 == null) {
                UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage0, ImageDisplayStatus.BackToBrowse);
            } else {
                mImage0.Source = mBitmapImage0;
                UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage0, ImageDisplayStatus.Available);
            }

            if (mBitmapImage1 == null) {
                UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage1, ImageDisplayStatus.BackToBrowse);
            } else {
                mImage1.Source = mBitmapImage1;
                UpdateImageDisplayStatus(ImageDisplayTarget.TwoPage1, ImageDisplayStatus.Available);
            }
        }

        private void UpdateImageDisplayStatus(ImageDisplayTarget target, ImageDisplayStatus status) {
            switch (target) {
            case ImageDisplayTarget.TwoPage0:
                mImage0.Visibility              = (status == ImageDisplayStatus.Available) ?    Visibility.Visible : Visibility.Collapsed;
                mImage0BackToBrowser.Visibility = (status == ImageDisplayStatus.BackToBrowse) ? Visibility.Visible : Visibility.Collapsed;
                mImage0NowLoading.Visibility    = (status == ImageDisplayStatus.NowLoading) ?   Visibility.Visible : Visibility.Collapsed;
                break;
            case ImageDisplayTarget.TwoPage1:
                mImage1.Visibility              = (status == ImageDisplayStatus.Available) ?    Visibility.Visible : Visibility.Collapsed;
                mImage1BackToBrowser.Visibility = (status == ImageDisplayStatus.BackToBrowse) ? Visibility.Visible : Visibility.Collapsed;
                mImage1NowLoading.Visibility    = (status == ImageDisplayStatus.NowLoading) ?   Visibility.Visible : Visibility.Collapsed;
                break;
            case ImageDisplayTarget.OnePage:
                mImageSingle.Visibility              = (status == ImageDisplayStatus.Available) ?    Visibility.Visible : Visibility.Collapsed;
                mImageSingleBackToBrowser.Visibility = (status == ImageDisplayStatus.BackToBrowse) ? Visibility.Visible : Visibility.Collapsed;
                mImageSingleNowLoading.Visibility    = (status == ImageDisplayStatus.NowLoading) ?   Visibility.Visible : Visibility.Collapsed;
                break;
            }
        }

        private BitmapImage CreateBitmapImageFromFileOrCache(string path) {
            var cachedEntry = mBitmapImageCache.Find(entry => 0 == path.CompareTo(entry.Path));
            if (cachedEntry != null) {
                return cachedEntry.BitmapImageCached;
            }

            byte[] fileContent = null;
            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {
                using (var br = new BinaryReader(fs)) {
                    //Console.WriteLine("{0}", fs.Length);
                    fileContent = br.ReadBytes((int)fs.Length);
                }
            }

            using (var ms = new MemoryStream(fileContent)) {
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.StreamSource = ms;
                bi.EndInit();
                bi.Freeze();

                mBitmapImageCache.Add(new BitmapImageCacheEntry(bi, path));
                if (BITMAP_CACHE_NUM < mBitmapImageCache.Count) {
                    mBitmapImageCache.RemoveAt(0);
                }
                return bi;
            }
        }

        private void LeafThroughNext() {
            Console.WriteLine("LeafThroughNext({0})", mCurrentPage);

            ReadAndShow(ShowCommand.NextPage, Dispatcher.CurrentDispatcher);
        }

        private void LeafThroughPrev() {
            Console.WriteLine("LeafThroughPrev({0})", mCurrentPage);

            ReadAndShow(ShowCommand.PrevPage, Dispatcher.CurrentDispatcher);
        }

        private void Advance1Page() {
            mCurrentPage += 1;

            Console.WriteLine("TwoPageViewAdvance1Page({0})", mCurrentPage);

            ReadAndShow(ShowCommand.SpecifiedPage, Dispatcher.CurrentDispatcher);
        }

        private void Back1Page() {
            mCurrentPage -= 1;

            Console.WriteLine("TwoPageViewBack1Page({0})", mCurrentPage);

            ReadAndShow(ShowCommand.SpecifiedPage, Dispatcher.CurrentDispatcher);
        }

        void OnImage1MouseUp(object sender, MouseButtonEventArgs e) {
            if (e.ChangedButton == MouseButton.Left) {
                LeafThroughNext();
            }
        }

        void OnImage0MouseUp(object sender, MouseButtonEventArgs e) {
            if (e.ChangedButton == MouseButton.Left) {
                LeafThroughPrev();
            }
        }

        void OnImageSingleMouseUp(object sender, MouseButtonEventArgs e) {
            var image = sender as Image;

            if (e.ChangedButton == MouseButton.Left) {
                var pos = e.GetPosition(image);
                if (pos.X < (image.ActualWidth / 2)) {
                    // left side is pressed
                    if (mCloseupViewGrid.FlowDirection == System.Windows.FlowDirection.LeftToRight) {
                        LeafThroughPrev();
                    } else {
                        LeafThroughNext();
                    }
                } else {
                    // right side is pressed
                    if (mCloseupViewGrid.FlowDirection == System.Windows.FlowDirection.LeftToRight) {
                        LeafThroughNext();
                    } else {
                        LeafThroughPrev();
                    }
                }
            }
        }

        private void OnBackToThumbnailBrowser(object sender, RoutedEventArgs e) {
            Console.WriteLine("OnBackToThumbnailBrowser");
            ChangeViewToThumbnailBrowser();
        }

        private void OnAdvance1Page(object sender, RoutedEventArgs e) {
            Advance1Page();
        }

        private void OnToggleFullscreen(object sender, RoutedEventArgs e) {
            switch (WindowState) {
            case System.Windows.WindowState.Maximized:
                // Windowed mode
                WindowState = System.Windows.WindowState.Normal;
                WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
                mThumbBrowseMenuItemFullscreen.IsChecked = false;
                mCloseupViewMenuItemFullscreen.IsChecked = false;
                break;
            case System.Windows.WindowState.Normal:
            default:
                // Fullscreen
                WindowState = System.Windows.WindowState.Maximized;
                WindowStyle = System.Windows.WindowStyle.None;
                mThumbBrowseMenuItemFullscreen.IsChecked = true;
                mCloseupViewMenuItemFullscreen.IsChecked = true;
                break;
            }
        }

        private void OnKeyUpOnCloseupView(KeyEventArgs e) {
            switch (e.Key) {
            case Key.Down:
            case Key.PageDown:
                LeafThroughNext();
                break;
            case Key.Up:
            case Key.PageUp:
                LeafThroughPrev();
                break;
            case Key.Left:
                Advance1Page();
                break;
            case Key.Right:
                Back1Page();
                break;
            case Key.Escape:
                UpdateViewsTreeThumbBrowse();
                break;
            }
        }

        private void OnMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) {
            if (0 < e.Delta) {
                LeafThroughPrev();
            } else if (e.Delta < 0) {
                LeafThroughNext();
            }
        }
    }
}






